package com.example.back.pay.yPay.directPurchase;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.back.bean.TopUpConfig;
import com.example.back.bean.UserInfo;
import com.example.back.bean.pay.UserPayInfo;
import com.example.back.dao.TopUpConfigDao;
import com.example.back.dao.UserDao;
import com.example.back.dao.monthlyCard.MonthlyCardDao;
import com.example.back.dao.pay.PayOrderInfoDao;
import com.example.back.util.RedisUtils;
import com.example.back.util.SendUtil;
import com.qq.open.OpensnsException;
import com.qq.open.SnsNetwork;
import com.qq.open.SnsSigCheck;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@RestController
public class OrderController {

    private static Logger logger = LoggerFactory.getLogger(OrderController.class);

    @Autowired
    private SendUtil sendUtil;

    @Autowired
    private UserDao userDao;

    @Autowired
    private PayOrderInfoDao payOrderInfoDao;

    @Autowired
    private TopUpConfigDao topUpConfigDao;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private MonthlyCardDao monthlyCardDao;

    @Value("${ysdk.appKey}")
    private  String appKey;

    @Value("${ysdk.placeOrderUrl}")
    private  String placeOrderUrl;

    @RequestMapping(value = "/order")
    public Map<String, Object> orderDiamond(String payType,String openid, String openkey, String userId, String type, String pf, String pfKey,String amounts,String diamonds) throws OpensnsException {

        Map<String,Object > codeInfo = new HashMap<>();
        Object userPay = redisUtils.get(userId + "pay");
        if(userPay!=null){
            logger.error("用户:{}在3秒内已经请求过了!",userId);
            codeInfo.put("code","403");
            codeInfo.put("message","请求繁忙");
            return codeInfo;
        }
        boolean ok = redisUtils.set(userId + "pay", "ok", 3);
        UserPayInfo userPayInfo = new UserPayInfo();
        logger.info("开始请求，参数openId={},openKey={},userId={},typ={},pf={},pfKey={},ok={}", openid, openkey, userId, type, pf, pfKey,ok);
        HashMap<String, String> params = new HashMap<>();//params类型不能是Map<String,String>会报错，需要改为HsahMap<String,String>
       /* if (StringUtils.isBlank(openid) && StringUtils.isBlank(openkey) && StringUtils.isBlank(type) && StringUtils.isBlank(userId)){
            params.put("4001","参数错误");
            return params;
        }*/
//       amounts = "6";
//       diamonds = "60";
        UserInfo userInfo = userDao.getOne(Long.parseLong(userId));

        logger.info("wxUserPay={}",userInfo==null);
        if (userInfo==null ){
            codeInfo.put("code","401");
            codeInfo.put("message","参数异常");
            return codeInfo;
        }
        if(type.equals("1") && userInfo.getWhetherPay()==1 ){
            codeInfo.put("code","402");
            codeInfo.put("message","玩家已经参加过首充活动");
            return codeInfo;
        }
        TopUpConfig topUpConfigs = null;
        //Type  1表示首充礼包，2表示幸运折扣，0表示充钻  3月卡充值
        Map<String,Object> map = null;
//        type="0";
        if ("1".equals(type)){
            Map<Object,Object> firstInfo = userDao.selectFirstCharge();
            logger.info("payParams={},1={}",amounts.equals(firstInfo.getOrDefault("amount",100).toString()),diamonds.equals(firstInfo.getOrDefault("number",88888).toString()));
            if (firstInfo==null || (!amounts.equals(firstInfo.getOrDefault("amount",100).toString()) && !diamonds.equals(firstInfo.getOrDefault("number",88888).toString()))){
                codeInfo.put("code","4001");
                codeInfo.put("message","首充参数异常");
                return codeInfo;
            }
            userPayInfo.setNumber(Long.valueOf( firstInfo.getOrDefault("number",88888).toString()));
            params.put("goodsmeta", "首冲"+diamonds+"钻石礼包*2元获得68钻石");//物品信息，格式必须是name*des，批量购买套餐时也只能有1个道具名称和1个描述，即给出该套餐的名称和描述
            amounts=firstInfo.getOrDefault("amount",6).toString();
        }else if ("2".equals(type)){
            String msgType = "LuckyPack"+amounts;
            String msgKey = "LuckyPack"+amounts;
            map = userDao.luckyDiscountMap(msgType,msgKey);
            if (map==null){
                codeInfo.put("code","4002");
                codeInfo.put("message","幸运折扣参数异常");
                return codeInfo;
            }
            com.alibaba.fastjson.JSONObject json = com.alibaba.fastjson.JSONObject.parseObject(map.get("msgValue").toString()) ;


            params.put("goodsmeta", "幸运折扣"+diamonds+"金币*288或者588幸运礼包");//物品信息，格式必须是name*des，批量购买套餐时也只能有1个道具名称和1个描述，即给出该套餐的名称和描述
            userPayInfo.setNumber(Long.valueOf(json.getString("gold")));
            amounts = json.getString("money");
        }else if ("0".equals(type)){

            String topUpConfigList = (String) redisUtils.get("allTopUpConfig");
            if(topUpConfigList!=null){
                List<TopUpConfig>  topUpConfigAll = JSON.parseArray(topUpConfigList,TopUpConfig.class);
                for (TopUpConfig topUpConfig : topUpConfigAll) {
                    if (topUpConfig.getDiamonds() == Integer.parseInt(diamonds) && topUpConfig.getMoney() == Integer.parseInt(amounts)) {
                        topUpConfigs = topUpConfig;
                        break;
                    }
                }
            }else {
                topUpConfigs = topUpConfigDao.getTopUpConfig(Integer.parseInt(amounts), Integer.parseInt(diamonds));
            }
            //     logger.info(topUpConfigList+"=====json");

            if (topUpConfigs == null) {
                codeInfo.put("code", "4000");
                codeInfo.put("message", "参数异常");
                return codeInfo;
            }
            userPayInfo.setNumber((long) topUpConfigs.getDiamonds() + (long) topUpConfigs.getExtra());
            params.put("goodsmeta", "充值"+userPayInfo.getNumber()+"钻石*"+topUpConfigs.getMoney()+"充值"+topUpConfigs.getDiamonds()+"钻石");//物品信息，格式必须是name*des，批量购买套餐时也只能有1个道具名称和1个描述，即给出该套餐的名称和描述
            amounts = topUpConfigs.getMoney().toString();
        }else if ("3".equals(type)){
            SimpleDateFormat formatters = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                map = userDao.luckyDiscountMap("MonthlyCard","MonthlyCard");
                if (map!=null && map.size()>0){

                    com.alibaba.fastjson.JSONObject json = com.alibaba.fastjson.JSONObject.parseObject(map.get("msgValue").toString()) ;


                    params.put("goodsmeta", "月卡充值*每日登录领取奖励");//物品信息，格式必须是name*des，批量购买套餐时也只能有1个道具名称和1个描述，即给出该套餐的名称和描述
                    userPayInfo.setNumber(0L);
                    amounts=json.getString("money");
                }else{
                    codeInfo.put("code","4003");
                    codeInfo.put("message","月卡充值异常");
                    return codeInfo;
                }
        }else{
            codeInfo.put("message","无效参数");
            return codeInfo;
        }



        //必填参数
        params.put("openid", openid);//app登陆获取
        params.put("openkey", openkey);//app登陆获取
        params.put("appid", "1109961678");//应用id
        params.put("ts", String.valueOf(System.currentTimeMillis() / 1000L));//时间戳（单位：秒）
        params.put("payitem", 1 + "*" + (Integer.parseInt(amounts)*10) + "*"+ 1);//使用x*p*num的格式，x表示物品ID，p表示单价（以Q点为单位，1Q币=10Q点，单价的制定需遵循腾讯定价规范），num表示默认的购买数量
//        params.put("payitem", 1 + "*" + Integer.parseInt(amounts)*10 + "*"+ 1);//使用x*p*num的格式，x表示物品ID，p表示单价（以Q点为单位，1Q币=10Q点，单价的制定需遵循腾讯定价规范），num表示默认的购买数量

        params.put("goodsurl", "http://.../coin.png");//物品的图片url(长度<512字符)
        params.put("pf", pf);//app登陆获取
        params.put("pfkey", pfKey);//app登陆获取
        params.put("zoneid", "1");//应用如果没有分区：传zoneid=1
        //可选参数
        //  params.put("amt", "1");//道具总价值。应用宝支持的qq支付和微信支付方式会有折扣（qq点和qq卡一般没有折扣），导致回调的amt数值与请求下单时的amt值不一致，所以回调避免用amt做金额验证，如有必要，可以在回调代码里采用Double.parseDouble(payitem.split("\\*")[1])的值做金额验证
        params.put("appmode", "1");//(可选)1表示用户不可以修改物品数量，2 表示用户可以选择购买物品的数量
        params.put("format", "json");//json、jsonp_$func。默认json。如果jsonp，前缀为：$func 例如：format=jsonp_sample_pay，返回格式前缀为：sample_pay()
        // params.put("userip", "");//用户ip  billno
        String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");
        //生成商户订单号
        params.put("app_metadata", uuid);//发货时透传给应用。长度必须<=128字符，可以传商户id，便于回调后续做验证

        //SnsSigCheck为腾讯开放平台提供的签名方法：[SDK下载](http://qzonestyle.gtimg.cn/qzone/vas/opensns/res/doc/Java_SDK_V3.0.6.zip)
        params.put("sig", SnsSigCheck.makeSig("POST", "/v3/r/mpay/buy_goods_m", params, appKey + "&"));//java调用签名方法必须用post请求,APP_KEY为app密钥
        //不必自己生成cookie直接调用官方工具包的方法，所以直接传入HashMap<String,String>作为cookie的配置即可
        HashMap<String, String> cookies = new HashMap<>();
        //1:QQ登录支付  ，2：微信登录支付
        if ("1".equals(payType)){
            cookies.put("session_id", "openid");     //QQ openid   wx hy_gameid
            cookies.put("session_type", "kp_actoken");  //QQ kp_actoken   wx wc_actoken
        }else if ("2".equals(payType)){
            cookies.put("session_id", "hy_gameid");     //QQ openid   wx hy_gameid
            cookies.put("session_type", "wc_actoken");  //QQ kp_actoken   wx wc_actoken
        }

        cookies.put("org_loc", SnsSigCheck.encodeUrl("/v3/r/mpay/buy_goods_m"));
        //https://ysdktest.qq.com/mpay/buy_goods_m为测试服务器
        String orderInfo = SnsNetwork.postRequest(placeOrderUrl, params, cookies, "http");

        logger.info("请求结果={}", orderInfo);
        JSONObject jsonObject = JSONObject.parseObject(orderInfo);
        Map<String, Object> result = jsonObject;
        if ("0".equals(result.getOrDefault("ret", 0).toString())) {

            userPayInfo.setUserId(userInfo.getUserId());
            userPayInfo.setOrderId(uuid);
            userPayInfo.setBillno(null);
            userPayInfo.setAmount(Integer.valueOf(amounts));
            userPayInfo.setStatus(0);
            userPayInfo.setType(Integer.parseInt(type));
            userPayInfo.setCreateTime(new Date());
            userPayInfo.setDetails(params.get("goodsmeta"));
            userPayInfo.setUserName(userInfo.getName());
            userPayInfo.setExt2(null);
            payOrderInfoDao.insertPayOrderInfo(userPayInfo);
            return result;
        }else if (!"0".equals(result.getOrDefault("ret", 0).toString())){
            return result;
        }
        return codeInfo;
    }

    //支付回调的接口必须部署在前面配置了证书的linux服务器上，并在应用宝配置界面做配置
    @RequestMapping(value = "/notify")
    @ResponseBody
    public String verify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logger.info("收到回调了");
        String url = request.getRequestURI();//请求uri
        HashMap<String, String> params = new HashMap<>();
        Enumeration em = request.getParameterNames();
        String req_sig = "";
        while (em.hasMoreElements()) {
            String key = (String) em.nextElement();
            String value = request.getParameter(key);
            if (key.equalsIgnoreCase("sig")) {
                req_sig = value;
            } else {
                params.put(key, encode(value));
            }
        }
        String appid = params.get("appid");//应用的唯一ID。可以通过appid查找APP基本信息。
        String appmeta = params.get("appmeta");//透传参数 可以用来获取商户订单号
        String billno = params.get("billno");//支付流水号（64个字符长度。该字段和openid合起来是唯一的）。
        String token = params.get("token");//应用调用v3/pay/buy_goods接口成功返回的交易token
        String payitem = params.get("payitem");//接收标准格式为ID*price*num   G001*10*1
        String sig = params.get("sig");//服务器签名
        String orderId = decode(appmeta).split("\\*")[0];//从appmeta中截取的透传参数商户支付流水号
        Double amount = Double.parseDouble(decode(payitem).split("\\*")[1]);
        String sys_sig = SnsSigCheck.makeSig("GET", "/api", params, appKey + "&");
        logger.info("回调签名检验req_sing={},sys_sig={}",req_sig,sys_sig);
        if (req_sig != null && req_sig.equalsIgnoreCase(sys_sig)) {

            //业务代码，自定义信息验证及数据库信息回写等
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
            String strDate = formatter.format(new Date());
            UserPayInfo userPayInfo = payOrderInfoDao.selectPayOrderInfo(orderId,strDate+" 00:00:00",strDate+" 23:59:59");
            if (userPayInfo!=null){

                UserInfo userInfo = userDao.getOne(userPayInfo.getUserId());
                logger.info("userInfo--->"+userInfo.toString());
                Map<String,String> map = new HashMap();
                map.put("type",17+"");
                map.put("IUserId",userPayInfo.getUserId()+"");
                logger.info("idStatus={},type={}",userPayInfo.getStatus(),userPayInfo.getType());
                if (userPayInfo.getStatus()==0){
                    if ("1".equals(String.valueOf(userPayInfo.getType()))){
                        map.put("funcType",1+"");
                        Map<Object,Object> firstInfo = userDao.selectFirstCharge();
                        map.put("diamonds",firstInfo.getOrDefault("number",10).toString());
                        //    userDao.updateUserFreeCardsInfo(payOrderDetails.getUserId(),Long.valueOf(firstInfo.getOrDefault("number",0).toString()));
                        userDao.updateShowUserDiamond(userPayInfo.getUserId(),Integer.parseInt(firstInfo.getOrDefault("number",10).toString()));
                    }else if ("2".equals(String.valueOf(userPayInfo.getType()))){
                        map.put("funcType",2+"");
                        map.put("gold",String.valueOf(userPayInfo.getNumber()));
                        String msgKey = "LuckyPack"+userPayInfo.getAmount();
                        String msgType = "LuckyPack"+userPayInfo.getAmount();
                        Map<String,Object> luckyMap = userDao.luckyDiscountMap(msgType,msgKey);
                        logger.info("payOrderUserId={},Quantity={},map={}",userPayInfo.getUserId(),userPayInfo.getOrderId(),luckyMap.size());
                        if (luckyMap!=null && luckyMap.size()>0){
                            userDao.updateLucky(userPayInfo.getNumber(),userPayInfo.getUserId());
                        }
                    }else if ("0".equals(String.valueOf(userPayInfo.getType()))){
                        map.put("funcType",0+"");
                        map.put("diamonds",String.valueOf(userPayInfo.getNumber()));
                        userDao.updateUserDiamond(userPayInfo.getUserId(),userPayInfo.getNumber());
                    }else if ("3".equals(String.valueOf(userPayInfo.getType()))){
                        map.put("funcType",3+"");
                        Calendar cal = Calendar.getInstance();
                        cal.add(Calendar.DATE, 30);
                        Date date = cal.getTime();
                        SimpleDateFormat formatters = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        String str = formatters.format(new Date());
                        logger.info("payUserId={},str={}",userPayInfo.getUserId(),str);
                        Map<String,Object> userMonthlyCard = monthlyCardDao.getUserAllMonthlyCardUser(userPayInfo.getUserId(),str);
                        logger.info("userMonthlyCard={}",userMonthlyCard);
                        if (userMonthlyCard!=null && userMonthlyCard.size()>0){
                            String endTime = userMonthlyCard.get("endTime").toString();
                            SimpleDateFormat fmt  = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                            String maxDateStr = String.valueOf(endTime);
                            String minDateStr = "";
                            Calendar calc =Calendar.getInstance();
                            try {
                                calc.setTime(fmt.parse(maxDateStr));
                                calc.add(calc.DATE, 30);
                                Date minDate = calc.getTime();
                                minDateStr = fmt.format(minDate);
                            } catch (ParseException e1) {
                                e1.printStackTrace();
                            }
                            monthlyCardDao.updateUserMonthlyCard(userPayInfo.getUserId(),Long.parseLong(userMonthlyCard.get("id").toString()),formatters.format(formatters.parse(minDateStr)));
                        }else{
                            monthlyCardDao.insertMonthlyCardUser(userPayInfo.getUserId(),str,formatters.format(date));
                        }



                    }
                    userPayInfo.setStatus(1);
                    userPayInfo.setBillno(billno);
                    payOrderInfoDao.updatePayOrderInfo(userPayInfo);
                }
                String send = sendUtil.send(userInfo.getEnterServer(), map);
                logger.info("验证成功----"+"send-->:"+send);
                return JSONObject.toJSONString(mkResultMap(0, "OK"));
            }else{
                return JSONObject.toJSONString(mkResultMap(-3, "系统繁忙"));
            }


        } else {
            logger.error("验证失败");
            return JSONObject.toJSONString(mkResultMap(-2, "签名错误"));//这里的ret值会影响之后腾讯给前端回调的错误码值，计算方式为500000-ret.例如此处ret=-2，回调错误码即为499998，ret=2，回调错误码为500002，利用这一点可以写出分类比较详细的错误验证
        }

    }

    public static Date addDays(Date d, int days)

    {

        d.setTime(d.getTime() + days * 1000 * 60 * 60 * 24);

        return d;

    }

    /**
     * 生成成功回复结果map
     *
     * @param ret 结果code
     * @param msg 结果msg
     */
    private Map<String, Object> mkResultMap(int ret, String msg) {
        Map<String, Object> map = new HashMap<>();
        map.put("ret", ret);
        map.put("msg", msg);
        return map;
    }

    /**
     * 解码腾讯编码规范
     *
     * @param string
     * @return
     */
    private String decode(String string) {
        // 替换ascii码为对应字符
        return string.replace("%2D", "-").replace("%2E", ".").replace("%5F", "_");
    }

    /**
     * 编码腾讯编码规范
     *
     * @param string
     * @return
     */
    private String encode(String string) {
        // 替换腾讯不建议字符为ascii码
        return string.replace("-", "%2D").replace(".", "%2E").replace("_", "%5F");
    }

}
